package com.hss.cn;

import java.math.BigDecimal;

/**
 * 高精度计算类
 * 用于进行一些高精度计算
 * 规避double、float精度问题导致计算不准确
 */
public class UtilMath {

    //除法运算精度
    private static final int DEFAULT_DIV_SCALE = 10;

    /**
     * 加法运算
     *
     * @param v1
     * @param v2
     * @param isHighPrecision 是否需要高精度计算，要：true，不要：false
     * @return
     */
    public static float add(float v1, float v2, boolean isHighPrecision) {
        if (!isHighPrecision) {
            return (v1 + v2);
        } else {
            BigDecimal bgNum1 = new BigDecimal(Float.toString(v1));
            BigDecimal bgNum2 = new BigDecimal(Float.toString(v2));
            return bgNum1.add(bgNum2).floatValue();
        }
    }

    /**
     * 减法运算
     *
     * @param v1
     * @param v2
     * @param isHighPrecision 是否需要高精度计算，要：true，不要：false
     * @return 运算结果
     */
    public static float sub(float v1, float v2, boolean isHighPrecision) {
        if (!isHighPrecision) {
            return (v1 - v2);
        } else {
            BigDecimal bgNum1 = new BigDecimal(Float.toString(v1));
            BigDecimal bgNum2 = new BigDecimal(Float.toString(v2));
            return bgNum1.subtract(bgNum2).floatValue();
        }
    }

    /**
     * 乘法运算
     *
     * @param v1
     * @param v2
     * @param isHighPrecision 是否需要高精度计算，要：true，不要：false
     * @return 运算结果
     */
    public static float mul(float v1, float v2, boolean isHighPrecision) {
        if (!isHighPrecision) {
            return (v1 * v2);
        } else {
            BigDecimal bgNum1 = new BigDecimal(Float.toString(v1));
            BigDecimal bgNum2 = new BigDecimal(Float.toString(v2));
            return bgNum1.multiply(bgNum2).floatValue();
        }
    }

    /**
     * 除法运算,当除不尽时，使用默认精度
     *
     * @param v1
     * @param v2
     * @return 运算结果
     */
    public static float div(float v1, float v2) {
        return div(v1, v2, DEFAULT_DIV_SCALE, true);
    }

    /**
     * 除法运算,当除不尽时，精确到小数点后scale位
     *
     * @param v1
     * @param v2
     * @param scale
     * @param isHighPrecision 是否需要高精度计算，要：true，不要：false
     * @return 运算结果
     */
    public static float div(float v1, float v2, int scale, boolean isHighPrecision) {
        if (scale < 0)
            throw new IllegalArgumentException("The scale must be a positive integer or zero");

        if (Float.compare(v2, 0.0f) == 0) return 0.0f;

        if (!isHighPrecision) {
            return (v1 / v2);
        } else {
            BigDecimal bgNum1 = new BigDecimal(Float.toString(v1));
            BigDecimal bgNum2 = new BigDecimal(Float.toString(v2));
            return bgNum1.divide(bgNum2, scale, BigDecimal.ROUND_HALF_UP).floatValue();
        }
    }

    /**
     * 四舍五入到小数点后scale位
     *
     * @param v
     * @param scale
     * @return
     */
    public static float round(float v, int scale) {
        if (scale < 0)
            throw new IllegalArgumentException("The scale must be a positive integer or zero");

        BigDecimal bgNum1 = new BigDecimal(Float.toString(v));
        BigDecimal bgNum2 = new BigDecimal("1");
        return bgNum1.divide(bgNum2, scale, BigDecimal.ROUND_HALF_UP).floatValue();
        // return b.setScale(scale, BigDecimal.ROUND_HALF_UP).floatValue();
    }

    /**
     * 加法运算
     *
     * @param v1
     * @param v2
     * @param isHighPrecision 是否需要高精度计算，要：true，不要：false
     * @return
     */
    public static double add(double v1, double v2, boolean isHighPrecision) {
        if (!isHighPrecision) {
            return (v1 + v2);
        } else {
            BigDecimal bgNum1 = new BigDecimal(Double.toString(v1));
            BigDecimal bgNum2 = new BigDecimal(Double.toString(v2));
            return bgNum1.add(bgNum2).doubleValue();
        }
    }

    /**
     * 减法运算
     *
     * @param v1
     * @param v2
     * @param isHighPrecision 是否需要高精度计算，要：true，不要：false
     * @return 运算结果
     */
    public static double sub(double v1, double v2, boolean isHighPrecision) {
        if (!isHighPrecision) {
            return (v1 - v2);
        } else {
            BigDecimal bgNum1 = new BigDecimal(Double.toString(v1));
            BigDecimal bgNum2 = new BigDecimal(Double.toString(v2));
            return bgNum1.subtract(bgNum2).doubleValue();
        }
    }

    /**
     * 乘法运算
     *
     * @param v1
     * @param v2
     * @param isHighPrecision 是否需要高精度计算，要：true，不要：false
     * @return 运算结果
     */
    public static double mul(double v1, double v2, boolean isHighPrecision) {
        if (!isHighPrecision) {
            return (v1 * v2);
        } else {
            BigDecimal bgNum1 = new BigDecimal(Double.toString(v1));
            BigDecimal bgNum2 = new BigDecimal(Double.toString(v2));
            return bgNum1.multiply(bgNum2).doubleValue();
        }
    }

    /**
     * 除法运算,当除不尽时，使用默认精度
     *
     * @param v1
     * @param v2
     * @return 运算结果
     */
    public static double div(double v1, double v2) {
        return div(v1, v2, DEFAULT_DIV_SCALE, true);
    }

    /**
     * 除法运算,当除不尽时，精确到小数点后scale位
     *
     * @param v1
     * @param v2
     * @param scale
     * @param isHighPrecision 是否需要高精度计算，要：true，不要：false
     * @return 运算结果
     */
    public static double div(double v1, double v2, int scale, boolean isHighPrecision) {
        if (scale < 0)
            throw new IllegalArgumentException("The scale must be a positive integer or zero");

        if (Double.compare(v2, 0d) == 0) return 0d;

        if (!isHighPrecision) {
            return (v1 / v2);
        } else {
            BigDecimal bgNum1 = new BigDecimal(Double.toString(v1));
            BigDecimal bgNum2 = new BigDecimal(Double.toString(v2));
            return bgNum1.divide(bgNum2, scale, BigDecimal.ROUND_HALF_UP).doubleValue();
        }
    }

    /**
     * 四舍五入到小数点后scale位
     *
     * @param v
     * @param scale
     * @return
     */
    public static double round(double v, int scale) {
        if (scale < 0)
            throw new IllegalArgumentException("The scale must be a positive integer or zero");

        BigDecimal bgNum1 = new BigDecimal(Double.toString(v));
        BigDecimal bgNum2 = new BigDecimal("1");
        return bgNum1.divide(bgNum2, scale, BigDecimal.ROUND_HALF_UP).doubleValue();
        // return b.setScale(scale, BigDecimal.ROUND_HALF_UP).floatValue();
    }


}
